﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Linq;

namespace Clmp.Data.DBEntity2
{
    /// <summary>
    /// 实体工厂
    /// </summary>
    /// <typeparam name="TEntityType">实体类型</typeparam>
    public class EntityFactory<TEntityType> : EntityFactoryBase
    //where TEntityType : IEntity
    {
        private static TypeBuilder typeBuilder = null;

        private EntityFactory()
        {

        }

        static EntityFactory()
        {
            if (AssBuilder == null)
            {
                AssemblyName assName = new AssemblyName();
                assName.Name = DynamicEntityNamespace;
                AssBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assName, AssemblyBuilderAccess.Run);
            }

            if (ModBuilder == null)
            {
                ModBuilder = AssBuilder.DefineDynamicModule(DynamicEntityNamespace);
            }

            if (typeBuilder == null)
            {
                //创建动态类型
                typeBuilder = ModBuilder.DefineType(DynamicEntityNamespace + ".Dynamic" + typeof(TEntityType).Name, TypeAttributes.Public);
                //typeBuilder.AddInterfaceImplementation(typeof(IEntity));

                //指定继承关系
                Type baseType = typeof(TEntityType);
                typeBuilder.SetParent(baseType);

                //构造属性
                Type keyValuesType = typeof(Dictionary<string, object>);
                PropertyInfo[] pis = typeof(TEntityType).GetProperties();

                bool hasAbstractPar = false;
                //创建属性
                foreach (PropertyInfo pi in pis)
                {
                    PropertyBuilder propBuilder = typeBuilder.DefineProperty(pi.Name, System.Reflection.PropertyAttributes.HasDefault, pi.PropertyType, null);
                    MethodAttributes getSetAttr = MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig;


                    if (pi.GetGetMethod().IsAbstract)
                    {
                        ColumnAttribute columnInfo;
                        object columnInfoObj = pi.GetCustomAttributes(typeof(ColumnAttribute), false).FirstOrDefault();
                        if (columnInfoObj == null)
                            columnInfo = new ColumnAttribute(pi.Name);
                        else
                            columnInfo = (ColumnAttribute)columnInfoObj;


                        #region 构造属性访问器
                        if (pi.CanRead)
                        {
                            //define getMethod
                            MethodBuilder getPropMethodBuilder = typeBuilder.DefineMethod("get_" + pi.Name, pi.GetGetMethod().Attributes & (~MethodAttributes.Abstract) | getSetAttr, pi.PropertyType, Type.EmptyTypes);
                            typeBuilder.DefineMethodOverride(getPropMethodBuilder, pi.GetGetMethod());
                            ILGenerator getPropMethodIL = getPropMethodBuilder.GetILGenerator();

                            var notFindVallabel = getPropMethodIL.DefineLabel();
                            var returnLavel = getPropMethodIL.DefineLabel();

                            var isFind = getPropMethodIL.DeclareLocal(typeof(bool));
                            var rtnVal = getPropMethodIL.DeclareLocal(pi.PropertyType);
                            var tempVal = getPropMethodIL.DeclareLocal(pi.PropertyType);

                            getPropMethodIL.Emit(OpCodes.Ldarg_0);
                            getPropMethodIL.Emit(OpCodes.Ldfld, baseType.GetField("PropertyDictionary", BindingFlags.Instance | BindingFlags.NonPublic));
                            getPropMethodIL.Emit(OpCodes.Ldstr, columnInfo.Name);

                            //获取索引
                            getPropMethodIL.Emit(OpCodes.Callvirt, keyValuesType.GetMethod("ContainsKey"));
                            getPropMethodIL.Emit(OpCodes.Stloc_S, isFind);
                            getPropMethodIL.Emit(OpCodes.Ldloc_S, isFind);

                            getPropMethodIL.Emit(OpCodes.Ldc_I4_0);
                            getPropMethodIL.Emit(OpCodes.Ceq);      //索引是否小于0

                            getPropMethodIL.Emit(OpCodes.Brtrue_S, notFindVallabel);

                            //key存在
                            if (pi.PropertyType.Name.IndexOf("Nullable") >= 0)
                            {
                                //可空类型支持
                                getPropMethodIL.Emit(OpCodes.Nop);
                                getPropMethodIL.Emit(OpCodes.Ldarg_0);
                                getPropMethodIL.Emit(OpCodes.Ldfld, baseType.GetField("PropertyDictionary", BindingFlags.Instance | BindingFlags.NonPublic));
                                getPropMethodIL.Emit(OpCodes.Ldstr, columnInfo.Name);
                                getPropMethodIL.Emit(OpCodes.Callvirt, keyValuesType.GetMethod("get_Item"));

                                getPropMethodIL.Emit(OpCodes.Unbox_Any, pi.PropertyType);
                                getPropMethodIL.Emit(OpCodes.Stloc_S, rtnVal);
                                getPropMethodIL.Emit(OpCodes.Br_S, returnLavel);
                            }
                            else
                            {
                                getPropMethodIL.Emit(OpCodes.Nop);
                                getPropMethodIL.Emit(OpCodes.Ldarg_0);
                                getPropMethodIL.Emit(OpCodes.Ldfld, baseType.GetField("PropertyDictionary", BindingFlags.Instance | BindingFlags.NonPublic));
                                getPropMethodIL.Emit(OpCodes.Ldstr, columnInfo.Name);
                                getPropMethodIL.Emit(OpCodes.Callvirt, keyValuesType.GetMethod("get_Item"));

                                if (pi.PropertyType.IsValueType)
                                {
                                    getPropMethodIL.Emit(OpCodes.Unbox_Any, pi.PropertyType);
                                }
                                else
                                {
                                    getPropMethodIL.Emit(OpCodes.Castclass, pi.PropertyType);
                                }
                                getPropMethodIL.Emit(OpCodes.Stloc_S, rtnVal);
                                getPropMethodIL.Emit(OpCodes.Br_S, returnLavel);
                            }

                            //key不存在
                            if (pi.PropertyType.Name.IndexOf("Nullable") >= 0)
                            {
                                //可空类型支持
                                getPropMethodIL.MarkLabel(notFindVallabel);
                                getPropMethodIL.Emit(OpCodes.Nop);
                                getPropMethodIL.Emit(OpCodes.Ldloca_S, tempVal);
                                getPropMethodIL.Emit(OpCodes.Initobj, pi.PropertyType);
                                getPropMethodIL.Emit(OpCodes.Ldloc_2);
                                getPropMethodIL.Emit(OpCodes.Stloc_1);
                                getPropMethodIL.Emit(OpCodes.Br_S, returnLavel);
                            }
                            else
                            {
                                getPropMethodIL.MarkLabel(notFindVallabel);
                                if (pi.PropertyType.IsValueType)
                                {
                                    getPropMethodIL.Emit(OpCodes.Ldc_I4_0);
                                    getPropMethodIL.Emit(OpCodes.Stloc_S, rtnVal);
                                }
                                else
                                {
                                    getPropMethodIL.Emit(OpCodes.Ldnull);
                                    getPropMethodIL.Emit(OpCodes.Stloc_S, rtnVal);
                                }
                                getPropMethodIL.Emit(OpCodes.Br_S, returnLavel);
                            }

                            //返回值
                            getPropMethodIL.MarkLabel(returnLavel);
                            getPropMethodIL.Emit(OpCodes.Ldloc_S, rtnVal);
                            getPropMethodIL.Emit(OpCodes.Ret);

                            propBuilder.SetGetMethod(getPropMethodBuilder);
                        }
                        #endregion 构造属性访问器

                        #region 构造属性设置器
                        //构造属性设置器
                        if (pi.CanWrite)
                        {
                            //define setMethod
                            MethodBuilder setPropMethodBuilder = typeBuilder.DefineMethod("set_" + pi.PropertyType, pi.GetSetMethod().Attributes & (~MethodAttributes.Abstract) | getSetAttr, null, new Type[] { pi.PropertyType });
                            typeBuilder.DefineMethodOverride(setPropMethodBuilder, pi.GetSetMethod());
                            ILGenerator setPropMethodIL = setPropMethodBuilder.GetILGenerator();
                            setPropMethodIL.Emit(OpCodes.Ldarg_0);
                            setPropMethodIL.Emit(OpCodes.Ldfld, baseType.GetField("PropertyDictionary", BindingFlags.Instance | BindingFlags.NonPublic));
                            setPropMethodIL.Emit(OpCodes.Ldstr, columnInfo.Name);
                            setPropMethodIL.Emit(OpCodes.Ldarg_1);
                            if (pi.PropertyType.IsValueType)
                            {
                                setPropMethodIL.Emit(OpCodes.Box, pi.PropertyType);
                            }
                            setPropMethodIL.Emit(OpCodes.Callvirt, keyValuesType.GetMethod("set_Item"));
                            setPropMethodIL.Emit(OpCodes.Ret);
                            propBuilder.SetSetMethod(setPropMethodBuilder);
                        }
                        #endregion 构造属性设置器

                        if (!hasAbstractPar)
                        {
                            hasAbstractPar = true;
                        }
                    }
                    if (!hasAbstractPar)
                        throw new CreateEntityException("实体必须至少包含一个抽象或虚拟属性。");

                }//创建属性结束

            }
        }
        /// <summary>
        /// 创建实体对象
        /// </summary>
        /// <returns>实体对象</returns>
        public static TEntityType CreateEntity()
        {
            return (TEntityType)Activator.CreateInstance(typeBuilder.CreateType());
        }
    }

    public abstract class EntityFactoryBase
    {
        protected static AssemblyBuilder AssBuilder;
        protected static ModuleBuilder ModBuilder;

        protected const string DynamicEntityNamespace = "Clmp.Data.DBEntity";
    }

    public class CreateEntityException : Exception
    {
        public CreateEntityException(string msg)
            : base(msg)
        {
        }
    }
}
